package net.xo68.destiny.core.rpc.impl;

import lombok.extern.slf4j.Slf4j;
import net.xo68.destiny.core.rpc.RpcClient;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.*;
import org.springframework.web.client.RestTemplate;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * rpc客户端实现
 * @author wuxie
 * @version 2020-11-5
 */
@Slf4j
public class RpcClientImpl implements RpcClient {

    private final String protocol;
    private final RestTemplate restTemplate;
    private final int retryCount;

    public RpcClientImpl(String protocol,RestTemplate restTemplate,int retryCount) {
        this.protocol=protocol;
        this.restTemplate = restTemplate;
        this.retryCount=retryCount;
    }

    @Override
    public int getRetryCount() {
        return retryCount;
    }

    /**
     *
     * @param host
     * @param url "/executor/log?logDateTime={1}&logId={2}&fromLineNum={3}"
     * @param uriVariables
     * @param <T>
     */
    @Override
    public <T> T get(String host, String url, Class<T> responseType, Object... uriVariables){
        StringBuilder sb=new StringBuilder();
        sb.append(protocol())
                .append(host)
                .append(url);
        //log.debug("请求地址：{}",sb.toString());
        try{
            return restTemplate.getForObject(sb.toString(),responseType,uriVariables);
        }catch (Exception e){
            log.warn("异常地址：{}", sb.toString());
            log.warn(e.getMessage(),e);
            throw new RuntimeException("执行端服务异常");
        }
    }
    @Override
    public <T,D> T postJson(String host, String url, Class<T> responseType, D d){
        StringBuilder sb=new StringBuilder();
        sb.append(protocol())
                .append(host)
                .append(url);
        //log.debug("请求地址：{}",sb.toString());

        HttpHeaders headers = new HttpHeaders();
        HttpMethod method = HttpMethod.POST;
        //提交方式
        headers.setContentType(MediaType.APPLICATION_JSON);
        //将请求头部和参数合成一个请求
        HttpEntity<D> requestEntity = new HttpEntity<>(d, headers);
        try{
            ResponseEntity<T> response =restTemplate.exchange(sb.toString(),method,requestEntity,responseType);
            return response.getBody();
        }catch (Exception e){
            log.warn("异常地址：{}", sb.toString());
            log.warn(e.getMessage(),e);
            throw new RuntimeException("执行端服务异常");
        }
    }

    @Override
    public <T> T tryGet(List<String> hosts, String defaultHost, String url, Class<T> responseType, Object... uriVariables) {
        if(hosts==null || hosts.isEmpty()){
            throw new IllegalArgumentException("hosts is null,or is empty");
        }
        int tryCount=0;
        String tryHost=defaultHost;
        final Set<String> tryedHostSet= new HashSet<>();
        if(StringUtils.isBlank(defaultHost)){
            int nodeIndex= RandomUtils.nextInt(0,hosts.size());
            tryHost=hosts.get(nodeIndex);
        }
        tryedHostSet.add(tryHost);
        while (tryCount < getRetryCount()){
            StringBuilder sb=new StringBuilder();
            sb.append(protocol())
                    .append(tryHost)
                    .append(url);
            //log.debug("请求地址：{}",sb.toString());
            try{
                return restTemplate.getForObject(sb.toString(),responseType,uriVariables);
            }catch (Exception e){
                log.warn("尝试次数：{},异常地址：{}",tryCount, sb.toString());
                //log.warn(e.getMessage(),e);
            }

            tryCount++;
            while (true){
                tryHost=hosts.get(RandomUtils.nextInt(0,hosts.size()));
                if(tryedHostSet.contains(tryHost) && hosts.size()==tryedHostSet.size()){
                    tryedHostSet.clear();
                    continue;
                }
                if(!tryedHostSet.contains(tryHost)){
                    break;
                }
            }

        }

        return null;
    }

    @Override
    public <T, D> T tryPostJson(List<String> hosts, String defaultHost, String url, Class<T> responseType, D d) {
        return tryPostJsonAndReturnHost(hosts,defaultHost,url,responseType,d).getReponseResult();
    }

    @Override
    public <T, D> TryRequestResult<T> tryPostJsonAndReturnHost(List<String> hosts, String defaultHost, String url, Class<T> responseType, D d) {
        if(hosts==null || hosts.isEmpty()){
            throw new IllegalArgumentException("hosts is null,or is empty");
        }
        int tryCount=0;
        String tryHost=defaultHost;
        final Set<String> tryedHostSet= new HashSet<>();
        if(StringUtils.isBlank(defaultHost)){
            int nodeIndex= RandomUtils.nextInt(0,hosts.size());
            tryHost=hosts.get(nodeIndex);
        }
        tryedHostSet.add(tryHost);

        HttpHeaders headers = new HttpHeaders();
        HttpMethod method = HttpMethod.POST;
        //提交方式
        headers.setContentType(MediaType.APPLICATION_JSON);

        TryRequestResult<T> requestResult=new TryRequestResult<>();

        while (tryCount < getRetryCount()){

            StringBuilder sb=new StringBuilder();
            sb.append(protocol())
                    .append(tryHost)
                    .append(url);
            //log.debug("请求地址：{}",sb.toString());
            try{
                //将请求头部和参数合成一个请求
                HttpEntity<D> requestEntity = new HttpEntity<>(d, headers);
                requestResult.setLastRequestHost(tryHost);
                ResponseEntity<T> response =restTemplate.exchange(sb.toString(),method,requestEntity,responseType);
                requestResult.setReponseResult(response.getBody());
                return requestResult;
            }catch (Exception e){
                log.warn("尝试次数：{},异常地址：{}",tryCount, sb.toString());
                //log.warn(e.getMessage(),e);
                requestResult.setReponseResult(null);
            }

            tryCount++;
            while (true){
                tryHost=hosts.get(RandomUtils.nextInt(0,hosts.size()));
                if(tryedHostSet.contains(tryHost) && hosts.size()==tryedHostSet.size()){
                    tryedHostSet.clear();
                    continue;
                }
                if(!tryedHostSet.contains(tryHost)){
                    break;
                }
            }

        }
        return requestResult;
    }

    @Override
    public String protocol() {
        return this.protocol;
    }
}
